﻿using DBLayer.Datacontext;
using ModelLayer;

// ***********************************************************************
// Assembly         : DBLayer
// Author           : Matthiesen, Jan (Kaj ved siden)
// Created          : 05-12-2014
//
// Last Modified By : Jan
// Last Modified On : 05-12-2014
// ***********************************************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace DBLayer
{
    public class DbUserOp
    {
        public DbUserOp()
        {
        }

        /// <summary>
        /// Converts ModelLayer.User to datacontext DBUser
        /// </summary>
        /// <param name="user">ModelLayer.user</param>
        /// <returns>A DBUser</returns>
        public DBUser ConvertUserToDbUser(User user)
        {
            DBUser dbuser = new DBUser();
            if (user.FirstName != null)
            {
                dbuser.fName = user.FirstName;
            }
            if (user.LastName != null)
            {
                dbuser.lname = user.LastName;
            }
            if (user.ProfileName != null)
            {
                dbuser.profileName = user.ProfileName;
            }
            if (user.Email != null)
            {
                dbuser.email = user.Email;
            }
            if (user.Email != null)
            {
                dbuser.status = (int)user.CurrentStatus;
            }

            //Always not null
            dbuser.gender = user.Gender;

            if (user.Password != null)
            {
                dbuser.password = user.Password;
            }

            return dbuser;
        }

        public User ConvertDbUserToUser(DBUser dbuser)
        {
            User user = new User();

            user.UserId = dbuser.userId;

            if (dbuser.fName != null)
            {
                user.FirstName = dbuser.fName;
            }
            if (dbuser.lname != null)
            {
                user.LastName = dbuser.lname;
            }
            if (dbuser.profileName != null)
            {
                user.ProfileName = dbuser.profileName;
            }
            if (dbuser.email != null)
            {
                user.Email = dbuser.email;
            }

            //Always not null
            user.Gender = dbuser.gender;

            if (dbuser.password != null)
            {
                user.Password = dbuser.password;
            }

            switch (dbuser.status)
            {
                case 0:
                    user.CurrentStatus = User.Status.Active;
                    break;

                case 1:
                    user.CurrentStatus = User.Status.InActive;
                    break;
            }

            return user;
        }

        private Friend ConvertDbFriendToFriend(DBFriend dbf)
        {
            User requested;
            if (dbf.fk_HigherUserId == dbf.fk_InviterId)
            {
                requested = GetUser(dbf.fk_LowerUserId);
            }
            else if (dbf.fk_LowerUserId == dbf.fk_InviterId)
            {
                requested = GetUser(dbf.fk_HigherUserId);
            }
            else
            {
                throw new Exception("Nobody requested this friendship.");
            }
            return new Friend() {Requester = GetUser(dbf.fk_InviterId), Requested = requested};

        }

        /// <summary>
        /// <para>Inserts a user into DB</para>
        /// <para>Returns:</para>
        ///     <para> 1 if successfull </para>
        ///     <para>-1 if an Exception was thrown</para>
        ///     <para>-2 if email already exists in DB</para>
        ///     <para>-3 if profile name already exists in DB</para>
        /// </summary>
        /// <param name="user">The user to be inserted</param>
        /// <returns>
        ///     an Int32
        /// </returns>
        public int InsertUser(User user)
        {
            var db = new RLADataContext();
            var dbUser = ConvertUserToDbUser(user);
            int result = -1;

            var existingUser = db.DBUsers.SingleOrDefault(x => x.email == user.Email);
            if (existingUser != null)
            {
                return -2;
            }
            existingUser = db.DBUsers.SingleOrDefault(x => x.profileName == user.ProfileName);
            if (existingUser != null)
            {
                return -3;
            }

            try
            {
                db.DBUsers.InsertOnSubmit(dbUser);
                db.SubmitChanges();
                result = dbUser.userId;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }

            return result;
        }

        public User GetUser(int userId)
        {
            RLADataContext db = new RLADataContext();
            var dbUser = db.DBUsers.SingleOrDefault(user => user.userId == userId);
            if (dbUser != null)
            {
                return ConvertDbUserToUser(dbUser);
            }
            return null;
        }

        public User GetUser(string userEmail)
        {
            RLADataContext db = new RLADataContext();
            var dbUser = db.DBUsers.SingleOrDefault(user => user.email == userEmail);
            return ConvertDbUserToUser(dbUser);
        }

        public User GetUser(string userEmail, string userPassword)
        {
            RLADataContext db = new RLADataContext();
            var dbUser = db.DBUsers.SingleOrDefault(user => user.email == userEmail && user.password == userPassword);

            if (dbUser != null)
            {
                return ConvertDbUserToUser(dbUser);
            }
            return null;
        }

        public List<User> GetFriendships(User user)
        {
            return GetFriendsWithStatus(user, Friend.Status.Accepted);
        }

        public List<Friend> GetPendingFriendships(User user)
        {
            RLADataContext db = new RLADataContext();
            List<DBFriend> dbFriends = db.DBFriends.Where(
                friend => (friend.fk_HigherUserId == user.UserId ||
                           friend.fk_LowerUserId == user.UserId) &&
                           friend.Status == (int)Friend.Status.Pending).ToList();

            List<Friend> friends = new List<Friend>();
            foreach (var dbFriend in dbFriends)
            {
                friends.Add(ConvertDbFriendToFriend(dbFriend));
            }

            return friends;
        }
        /*public List<User> GetPendingFriendships(User user)
        {
            return GetFriendsWithStatus(user, Friend.Status.Pending);
        }*/
        private List<User> GetFriendsWithStatus(User user, Friend.Status status)
        {
            RLADataContext db = new RLADataContext();
            List<DBFriend> dbFriends = db.DBFriends.Where(
                friend => (friend.fk_HigherUserId == user.UserId ||
                           friend.fk_LowerUserId == user.UserId) &&
                           friend.Status == (int)status).ToList();

            int[] friendIDs = new int[dbFriends.Count];

            for (int i = 0; i < dbFriends.Count; i++)
            {
                DBFriend dbFriend = dbFriends[i];

                int friendId = dbFriend.fk_HigherUserId == user.UserId
                    ? dbFriend.fk_LowerUserId
                    : dbFriend.fk_HigherUserId;

                friendIDs[i] = friendId;
            }

            return GetUsers(friendIDs);
        }

        public int RequestFriendship(User requester, User requested, Friend.Status status = Friend.Status.Pending)
        {
            RLADataContext db = new RLADataContext();
            int ret = -1;
            if (requester.UserId != requested.UserId)
            {
                int highId = requester.UserId > requested.UserId ? requester.UserId : requested.UserId;
                int lowId = requester.UserId == highId ? requested.UserId : requester.UserId;

                if (db.DBFriends.FirstOrDefault((x) => x.fk_HigherUserId == highId && x.fk_LowerUserId == lowId) == null)
                {
                    if (requested.CurrentStatus == User.Status.Active)
                    {
                        DBFriend dbf = new DBFriend()
                                       {
                                           fk_InviterId = requester.UserId,
                                           fk_HigherUserId = highId,
                                           fk_LowerUserId = lowId
                                       };
                        dbf.Status = (int)status;
                        db.DBFriends.InsertOnSubmit(dbf);
                        db.SubmitChanges();
                        ret = 1;
                    }
                    else
                    {
                        ret = -2;
                    }
                }
            }
            else
            {
                ret = -3;
            }

            return ret;
        }

        private List<User> GetUsers(int[] userIDs)
        {
            RLADataContext db = new RLADataContext();

            List<DBUser> dbUsers = db.DBUsers.Where(user => userIDs.Contains(user.userId) && user.status == (int)User.Status.Active).ToList();
            List<User> users = dbUsers.Select(dbUser => ConvertDbUserToUser(dbUser)).ToList();
            return users;
        }

        public List<User> SearchUserList(string[] keywords)
        {
            RLADataContext db = new RLADataContext();

            List<DBUser> dbUsers = new List<DBUser>();
            List<DBUser> temp = new List<DBUser>();

            foreach (var keyword in keywords)
            {
                temp = (from x in db.DBUsers
                        where x.fName.Contains(keyword)
                       || x.lname.Contains(keyword)
                       || x.profileName.Contains(keyword)
                        select x).ToList();

                dbUsers.AddRange(temp);
            }

            List<DBUser> distinctDbUsers = dbUsers
     .GroupBy(p => p.profileName)
     .Select(g => g.First())
     .ToList();

            List<User> users = new List<User>();

            foreach (DBUser dbU in distinctDbUsers)
            {
                users.Add(ConvertDbUserToUser(dbU));
            }

            return users;
        }

        public int ChangeFriendshipStatus(User requester, User accepter, Friend.Status status)
        {
            RLADataContext db = new RLADataContext();

            int lowerId = requester.UserId > accepter.UserId ? accepter.UserId : requester.UserId;

            int higherId = requester.UserId == lowerId ? accepter.UserId : requester.UserId;

            DBFriend dbfriend = db.DBFriends.SingleOrDefault(friend => friend.fk_HigherUserId == higherId && friend.fk_LowerUserId == lowerId);

            int result = -1;  //<-- something else went wrong

            if (dbfriend != null)
            {
                dbfriend.Status = (int)status;
                try
                {
                    db.SubmitChanges();
                    result = 1;
                }
                catch (Exception e)
                {
                    result = -2; //<-- an exception was thrown
                }
            }
            else
            {
                result = -3;//<-- friendship not found in DB
            }

            return result;
        }

        /// <summary>
        ///  Deletes a list of users from db (using userID).
        /// </summary>
        /// <param name="users">The users to be deleted</param>
        /// <returns>
        ///     <para> 1 if successfull</para>
        ///     <para>-1 if list from DB == null</para>
        ///     <para>-2 if list from DB is empty</para>
        ///     <para>-3 if an exception occured</para>
        /// </returns>
        public int DeleteUsers(int[] userIDs)
        {
          int errorCode = 0;

          try
          {
            RLADataContext db = new RLADataContext();
            List<DBUser> dbUsers = db.DBUsers.Where(user => userIDs.Contains(user.userId)).ToList();

            if (dbUsers == null)
            {
              errorCode = -1;
            }
            else if (dbUsers.Count == 0)
            {
              errorCode = -2;
            }
            else
            {

              db.DBUsers.DeleteAllOnSubmit(db.DBUsers.Where(user => userIDs.Contains(user.userId)));
              db.SubmitChanges();
              errorCode = 1;
            }
          }
          catch (Exception e)
          {
            errorCode = -3;
          }

            return errorCode;
        }

    }
}